// Copyright 2012 dmitry.a.novikov@gmail.com
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Templates implementing refcounter methods


#ifndef _INCLUDED_CNZ_OBJECTIMPL_H_
#define _INCLUDED_CNZ_OBJECTIMPL_H_

#include "exception.h"

namespace CnsZ {

template<class T>
class ImplNoRefCount : public T {
public:
  void addRef() const {}
  void release() const {}
  int numRefs() const { return 1; }
};

template<class T, class N=T>
class ImplRefCount : public T {
  struct Helper {
    Helper() : refcounter(1) {}
    mutable RefCounter refcounter;
    ImplRefCount<T, N> refcnt_payload_object;
  };
  const Helper* helper() const { return (Helper*)(((Byte*)this) - offsetof(Helper, refcnt_payload_object)); }

  ImplRefCount(const ImplRefCount&); // prohibited
  void operator=(const ImplRefCount&); // prohibited
protected:
  ImplRefCount() {}
public:

  void addRef() const { ++helper()->refcounter; }

  void release() const {
    if(!--helper()->refcounter) {
      delete helper();
    }
  }

  int numRefs() const { return helper()->refcounter; }

  static P<N> New() {
    return P<N>(&(new Helper())->refcnt_payload_object, DoNotAddRef);
  }

  static P<N> New(size_t addmemsize) {
    void* memory = new UInt8[sizeof(Helper)+addmemsize];
    return P<N>(&(new (memory) Helper())->refcnt_payload_object, DoNotAddRef);
  }
};

template<class TObj, class TImpl>
class DObjectWithImpl : public TObj {
  TImpl impl;
protected:
  DObjectWithImpl() : TObj(&impl) {}
public:
  ~DObjectWithImpl() {
    DObject::impl = 0; // avoid deletition in ~DObject()
  }
  static P<TObj> New() {
    return P<TObj>(new DObjectWithImpl(), DoNotAddRef);
  }
};

#define NULL_FACTORY(Cls) throw NullFactory( Cls::classInfo()->name() )

#define TEMPL_IMPL_FACTORY(Cls) return ImplRefCount<##Cls##Impl<Cls>, Cls>::New()

#define IMPL_FACTORY(Cls) return ImplRefCount<##Cls##Impl, Cls>::New()

#define OBJECTIMPL_FACTORY(Cls) return ImplRefCount<Cls, Cls>::New()

#define DEFINE_CNSZ_OBJECT_CLASS( Cls, FACTORY ) \
  const Class* CnsZ::##Cls##::isA() const { return classInfo(); } \
  \
  CNSZ_DEFINE_STATIC_ASTRING(g_class_##Cls##_name, #Cls);\
  \
  class CnsZ_##Cls##_ClassImpl : public ImplNoRefCount<Class> {\
  public:\
    const AString& name() const { return CNSZ_GET_STATIC_ASTRING(g_class_##Cls##_name); }\
    P<Object> createInstance() const;\
    const Class* parentClass() const;\
  };\
  static CnsZ_##Cls##_ClassImpl s_CnsZ_##Cls##_info;\
  \
  const Class* CnsZ::##Cls##::classInfo() {\
    return &s_CnsZ_##Cls##_info;\
  }

#define DEFINE_CNSZ_CLASS( Cls, Parent, FACTORY ) \
  DEFINE_CNSZ_OBJECT_CLASS( Cls, FACTORY ) \
  const Class* CnsZ_##Cls##_ClassImpl::parentClass() const { return Parent::classInfo(); }\
  \
  P<Object> CnsZ_##Cls##_ClassImpl::createInstance() const { return Cls::New().get(); }\
  \
  P<Cls> Cls::New() { FACTORY(Cls); }

}

#define DECLARE_CNSZ_DBCLASS(ClassName) \
  protected:\
    friend class ClassName##Impl;\
    ClassName(##ClassName##Impl* impl);\
  public:\
    DECLARE_CNSZ_CLASS(ClassName)

#define DEFINE_CNSZ_DBCLASS( Class, Parent, FACTORY ) \
  DEFINE_CNSZ_CLASS(Class, Parent, FACTORY) \
  Class::Class(##Class##Impl* imp) : Parent(imp) {}

#define DBOBJECT_FACTORY(Cls) return DObjectWithImpl<Cls, ##Cls##Impl>::New()

#endif //#ifndef _INCLUDED_CNZ_OBJECTIMPL_H_